Moving Things Around
You have much more control over the rendering than this, however. Change your Draw call to the following and run the application:
spriteBatch.Draw(texture, new Vector2(100,100),
null, Color.White, 0.3f, Vector2.Zero, 1.0f,
SpriteEffects.None, 1.0f);
Notice that the cat is drawn
with the upper, left corner at 100,100 as you specified in the position
vector, but it is now rotated slightly. This overload is more complex
than the previous ones, so let’s look at the new parameters
individually.
The first new parameter is
the third one, which is listed as null. This is the source rectangle,
and unlike the destination rectangle, it controls what you draw rather
than where you draw (discussed more in depth later in the chapter).
Directly after the tint color is type float, which is the rotation
angle you want to render at. This angle is specified in radians.
Note
To convert angles in degrees to radians, use the MathHelper.ToRadians method.
The next parameter is the
origin (another vector), which we discuss in a moment. Directly after
the origin is the scale parameter, which you use to uniformly scale the
image. A scale of 1.0f is normal size, 2.0f is twice the size, and 0.5f
is half the size.
Next up is the SpriteEffects enumeration, which has three options: None, FlipHorizontally, and FlipVertically. Each of these do exactly as the name implies. Passing in None
does no special processing of the image, whereas passing in either of
the other two flips the image before drawing it, either horizontally or
vertically. For example, if you use SpriteEffects.FlipVertically, the cat is drawn upside down.
The final parameter is
called the layer depth. This value should be between 0.0f and 1.0f with
1.0f is “on top” and 0.0f is “on bottom.” This enables you to control
how the images are sorted when you draw more than one, and it is
ignored unless the sort mode is set to either BackToFront or FrontToBack. We talk about sort modes later in the chapter.
This is the largest overload,
and it has every feature you need in drawing a sprite. There is another
overload that has the same number of parameters, but replaces the
single scale parameter with a Vector2.
This enables you to scale your image with different scaling values for
the X and Y axis. So if you passed in a scale vector of 2.0f, 1.0f, the
image would be twice as wide, but the same height. A scale vector of
0.5f, 2.0f causes the image to be half as wide, but twice the height.
Now it is time to go
back to the mysterious origin parameter. The origin is the point around
which the rotation occurs. In the previous example, you used Vector2.Zero, so your rotation is around the upper left corner of the image. If you use new Vector2(texture.Width/2, texture.Height/2) instead, the image rotates around the center (see Figure 2).
Animation
There is only one parameter to the Draw
overloads left to discuss, which is the source rectangle. As mentioned,
the source rectangle lets you control the portion of the image you
draw. Up until now, you drew the entire image, which is the default
behavior if this parameter is not specified or if it is null. What if
you had a single image that had multiple smaller images inside it, and
you only wanted to draw a portion of it? That is what the source
rectangle is for. The cat image is 256×256 pixels, but if you specified
a source rectangle of (0,0,256,128), it renders the top portion of the
cat and not the bottom portion.
One common usage of the
source rectangle is for simple 2D animations. Your image contains
several “frames” of animation that you swap through in succession to
give the illusion of motion. Cartoons have been doing this for years,
and it is a common technique.
In your content project,
add another existing item and this time, include the
spriteanimation.png image from the downloadable examples. Like before,
update the LoadContent method to change which texture you load.
texture = Content.Load<Texture2D>("spriteanimation");
If you run the project
now, you can see a weird image. It looks like there are several images
instead of just a single one. That is exactly what the image is—a lot
of smaller images stored in a larger one. In this case, there are ten
separate 96×96 images stored within a single 960×96 image. If you
change the Draw call in the Draw method as in the following, a single image is now drawn:
spriteBatch.Draw(texture, new Vector2(100, 100),
new Rectangle(0, 0, 96, 96), Color.White);
The source rectangle of
0,0,96,96 tells the sprite batch to render only from the first smaller
image within the file and to ignore the rest. By itself, though, it is
still bland. Replace the Draw call with the following code to see the animation play out:
int frame = (int)(gameTime.TotalGameTime.TotalSeconds * 20) % 10;
spriteBatch.Draw(texture, new Vector2(100, 100),
new Rectangle(frame * 96, 0, 96, 96), Color.White);
Now you see a guy running in
place at a position of 100,100! What you’ve done here is select which
portion of the source image to draw from based on the current amount of
time the game has run. You’ve taken the total number of seconds the
game has run (this is a fractional value, so it records portions of a
second, too), and multiplied it by 20, which forces the animation to
run 20 times per second. You then pick the current frame by doing a
modulus against the total number of frames and some quick math to pick
the correct source rectangle based on that frame.
This technique is a common way 2D games render animations.